package com.chinaums.study.restful.service;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

@Service
public class RestfulService {

    private static final String HOST = "172.16.208.24";

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final RestTemplate restTemplate;

    private volatile String sessionId;

    private volatile long lastAccess;

    public RestfulService(@Autowired RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    /**
     * 登录人脸识别系统，获取会话ID
     * @return session_id
     */
    private synchronized void login() {
        if (sessionId == null) {
            final Map<String, String> params = new HashMap<>();
            params.put("name", "developer");
            params.put("password", DigestUtils.md5Hex("dev2017").toLowerCase());
            final Map res = restTemplate.postForObject(getUrl(7500, "resource_manager/user/login"), params, Map.class);
            if (!res.get("rtn").equals(0)) {
                logger.warn("登录失败：{}", res);
                throw new RuntimeException("登录失败：" + res.get("message"));
            }
            sessionId = res.get("session_id").toString();
        }
        lastAccess = System.currentTimeMillis();
    }

    @Scheduled(cron = "0 * * * * ?")
    private void clearSession() {
        if (System.currentTimeMillis() - lastAccess > 60000) {
            sessionId = null;
        }
    }

    /**
     * 上传需要比对的照片，并获取照片在图像库中的ID
     * @param pic
     * @return face_image_id
     * @throws IOException
     */
    public Long upload(final InputStream pic) throws IOException {
        login();
        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        IOUtils.copy(pic, bytes);
        final Map<String, String> req = new HashMap<>();
        req.put("picture_image_content_base64", Base64.getEncoder().encodeToString(bytes.toByteArray()));
        final HttpHeaders headers = new HttpHeaders();
        headers.set("session_id", sessionId);
        final HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(req, headers);
        final Map<?, ?> res = restTemplate.postForObject(getUrl(9100, "face/v1/framework/face_image/repository/picture/synchronized"), requestEntity, Map.class);
        if (!res.get("rtn").equals(0)) {
            logger.warn("图片上传失败：{}", res);
            throw new RuntimeException("图片上传失败：" + res.get("message"));
        }
        final Map<?, ?> result = (Map<?, ?>) ((List<?>) res.get("results")).get(0);
        return (Long) result.get("face_image_id");
    }

    /**
     * 照片比对，并返回相似度满足要求的照片
     * @param faceImageId
     * @return
     */
    public List<?> retrieval(final Long faceImageId, final List<Integer> repos) {
        login();
        final Map<String, Object> reqRetrieval = new HashMap<>();
        reqRetrieval.put("face_image_id", faceImageId);
        reqRetrieval.put("repository_ids", repos);
        reqRetrieval.put("threshold", 80);
        final Map<String, Object> reqOrder = new HashMap<>();
        reqOrder.put("similarity", -1);
        final Map<String, Object> req = new HashMap<>();
        req.put("retrieval", reqRetrieval);
        req.put("fields", Arrays.asList("similarity", "picture_url", "face_image_url", "repository_id", "name", "gender"));
        req.put("condition", new HashMap<>());
        req.put("order", reqOrder);
        req.put("start", 0);
        req.put("limit", 10);
        final HttpHeaders headers = new HttpHeaders();
        headers.set("session_id", sessionId);
        final HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(req, headers);
        final Map<?, ?> res = restTemplate.postForObject(getUrl(9200, "face/v1/framework/face/retrieval"), requestEntity, Map.class);
        if (!res.get("rtn").equals(0)) {
            logger.warn("图片比对失败：{}", res);
            throw new RuntimeException("图片比对失败：" + res.get("message"));
        }
        final List<?> results = (List<?>) res.get("results");
        results.stream().map(i -> (Map) i)
            .forEach(i -> {
                final String uri = i.get("picture_uri").toString();
                final String url = getUrl(7300, "storage/v1/image?uri_base64=" + Base64.getEncoder().encodeToString(uri.getBytes()));
                i.put("picture_uri", url);
            });
        return results;
    }

    public List<Map<String, Object>> loadRepos() {
        login();
        final HttpHeaders headers = new HttpHeaders();
        headers.set("session_id", sessionId);
        final HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(headers);
        final ResponseEntity<Map> res = restTemplate.exchange(getUrl(11180, "face/v1/framework/face_image/repository"), HttpMethod.GET, requestEntity, Map.class);
        if (!res.getBody().get("rtn").equals(0)) {
            logger.warn("图片比对失败：{}", res);
            throw new RuntimeException("图片比对失败：" + res.getBody().get("message"));
        }
        return (List) res.getBody().get("results");
    }

    private String getUrl(final int port, final String requestPath) {
        return "http://" + HOST + ":" + port + "/" + requestPath;
    }

}
